<?php
/** 
 * @brief 数据库表操作类 
 */
class Db_Table {
    /** 
     * 数据库驱动实例
     *
     * @var object 
     */
    protected $db;

    /** 
     * 表名称
     *
     * @var string 
     */
    public $tableName = '';
	
    /** 
     * 主键字段(默认为'id')
     *
     * @var string 
     */
    public $primaryKey = 'id';

	/**
	 * 插入新记录的时间字段名
	 *
	 * @var string
	 */
	public $createdCol = '';

	/**
	 * 最后更改记录的时间字段名
	 *
	 * @var string
	 */
	public $updatedCol = '';

    /**
     * 构造函数 
     * 
     * @note 取得数据库驱动实例
     * @param string $dsnName DSN配置名称
     * 
     * @return void
     */
	public function __construct($dsnName = null) {
        if (empty($this->tableName)) {
            $this->tableName = strtolower(str_replace('_Model', '', get_class($this)));
        }
        
        if ($dsnName) {
            $this->db = Db::connect($dsnName);
            $this->tableName = $this->db->tablePrefix . $this->tableName;
        }
    }

    /**
     * 解析条件参数 
     * 
     * @param mixed $where 条件参数
     * 
     * @return mixed
     */
	protected function _where($where = null) {
        if ($where) {
            if (is_array($where)) {
                $value = isset($where[1]) ? $where[1] : null;
                return [$where[0], $value];
            } else {
                return [$this->primaryKey . ' = ?', $where];
            }
        }
        return null;
	}

    /**
     * 根据相关参数生成SQL查询语句和绑定参数
     * 
     * @param string $fields 查询字段
     * @param mixed $where 条件参数
     * @param string $order 排序参数
     * 
     * @return array
     */
	protected function _find($fields = '*', $where = null, $order = '') {
        $sqlTpl = 'SELECT %s FROM %s';
        $sql = sprintf($sqlTpl, $fields, $this->tableName);
        $where = $this->_where($where);
		if ($where) {
			$sql .= sprintf(' WHERE %s', $where[0]);
		}
		if ($order) {
            $sql .= sprintf(' ORDER BY %s', $order);
		}
		return ['sql' => $sql, 'params' => (array) $where[1]];
	}

    /**
     * 查询并返回从某个位置开始且指定行数的记录 
     * 
     * @param string $fields 查询字段
     * @param mixed $where 条件参数
     * @param string $order 排序参数
     * @param int $count 最大行数
     * @param int $offset 偏移量
     * 
     * @return array
     */
	public function limit($fields, $where, $order, $count = 10, $offset = 0) {
        $result = $this->_find($fields, $where, $order);
        return $this->db->selectLimit($result['sql'], $result['params'], (int) $count, (int) $offset);
	}
    
    /**
     * 查询并返回指定某一页的记录
     * 
     * @param int $page 当前页码
     * @param string $fields 查询字段
     * @param mixed $where 条件参数
     * @param string $order 排序参数
     * @param int $pageSize 每页显示的记录数
     * 
     * @return array
     */
	public function findPage($page, $fields = '*', $where = null, $order = '', $pageSize = 10) {
        $page = ($page > 0) ? $page : 1;
        $offset = ($page - 1) * $pageSize;
        return $this->limit($fields, $where, $order, $pageSize, $offset);
	}
    
    /**
     * 查询并回传所有记录 
     * 
     * @param string $fields 查询字段
     * @param mixed $where 条件参数
     * @param string $order 排序参数
     * 
     * @return array
     */
	public function findAll($fields = '*', $where = null, $order = '') {
        $result = $this->_find($fields, $where, $order);
        return $this->db->getAll($result['sql'], $result['params']);
	}

    /**
     * 查询并回传第一条记录的指定字段的值 
     * 
     * @param string $field 查询字段
     * @param mixed $where 条件参数
     * @param string $order 排序参数
     * 
     * @return mixed
     */
	public function findCol($field, $where = null, $order = '') {
        $result = $this->_find($field, $where, $order);
        return $this->db->getOne($result['sql'], $result['params']);
	}

    /**
     * 查询并回传第一条记录 
     * 
     * @param mixed $where 条件参数
     * @param string $fields 查询字段
     * @param string $order 排序参数
     * 
     * @return array
     */
	public function find($where = null, $fields = '*', $order = '') {
        $result = $this->_find($fields, $where, $order);
        return $this->db->getRow($result['sql'], $result['params']);
	}

    /**
     * 插入新记录 
     * 
     * @note 返回所插入的这条记录的主键值
     * @param array $params 字段参数
     * 
     * @return mixed
     */
	public function create($params = []) {  
		$sqlTpl = 'INSERT INTO %s (%s) VALUES (%s)';
		foreach ($params as $field => $value) {
			$fields[] = $field;
			$placeholders[] = '?';
			$values[] = $value;
		}
        if ($this->createdCol && !isset($params[$this->createdCol])) {
            $fields[] = $this->createdCol;
            $placeholders[] = '?';
            $values[] = date('Y-m-d H:i:s', time());
        }
        if ($this->updatedCol && !isset($params[$this->updatedCol])) {
            $fields[] = $this->updatedCol;
            $placeholders[] = '?';
            $values[] = date('Y-m-d H:i:s', time());
        }
		$sql = sprintf($sqlTpl, $this->tableName, implode(', ', (array) $fields), implode(', ', (array) $placeholders));
		$this->db->execute($sql, (array) $values);
		return $this->db->insertId();
	}
    
    /**
     * 更改记录 
     * 
     * @note 返回执行该次查询后所受影响的记录数
     * @param mixed $where 条件参数
     * @param array $params 字段参数
     * 
     * @return int
     */
	public function update($where, array $params) {
        if (isset($params[$this->primaryKey])) {
            unset($params[$this->primaryKey]);
        }

		$sqlTpl = 'UPDATE %s SET %s WHERE %s';
		foreach ($params as $field => $value) {
			$fields[] = "{$field} = ?";
			$values[] = $value;
		}
        if ($this->updatedCol && !isset($params[$this->updatedCol])) {
            $fields[] = $this->updatedCol . ' = ?';
            $values[] = date('Y-m-d H:i:s', time());
        }
		$where = $this->_where($where);
		$sql = sprintf($sqlTpl, $this->tableName, implode(', ', (array) $fields), $where[0]);
        $values = array_merge((array) $values, (array) $where[1]);
		return $this->db->execute($sql, $values);
	}
	
    /**
     * 删除记录
     * 
     * @note 返回执行该次查询后所受影响的记录数
     * @param mixed $where 条件参数
     * 
     * @return int
     */
	public function delete($where = null) {
		$sqlTpl = 'DELETE FROM %s WHERE %s';
        $where = $this->_where($where);
		$sql = sprintf($sqlTpl, $this->tableName, $where[0]);
		return $this->db->execute($sql, (array) $where[1]);
	}
}
